/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.exporter;

import cn.easyplatform.messages.vos.datalist.ListHeaderVo;
import cn.easyplatform.messages.vos.datalist.ListVo;
import org.zkoss.zk.ui.Component;
import org.zkoss.zul.*;
import org.zkoss.zul.impl.MeshElement;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static cn.easyplatform.web.exporter.util.Utils.invokeComponentGetter;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractExporter<E, T> {

    protected Interceptor<E> _interceptor;

    /**
     * 导出复合表头
     *
     * @param columnSize
     * @param auxhead
     * @param e
     */
    protected abstract void exportAuxhead(int columnSize, Auxhead auxhead, E e);

    protected abstract void exportAuxhead(ListVo entity,
                                          int columnSize, Auxhead auxhead, E e);

    /**
     * 导出表头
     *
     * @param component
     * @param e
     */

    protected abstract void exportColumnHeaders(Component component, E e);

    protected abstract void exportColumnHeaders(List<ListHeaderVo> headers,
                                                Component component, E e);

    /**
     * 导出分组头
     *
     * @param columnSize
     * @param group
     * @param e
     */
    protected abstract void exportGroup(int columnSize, Component group, E e);

    /**
     * 导出分组尾
     *
     * @param columnSize
     * @param groupfoot
     * @param e
     */
    protected abstract void exportGroupfoot(int columnSize, Component groupfoot, E e);

    /**
     * 导出表格
     *
     * @param rowIndex
     * @param columnSize
     * @param row
     * @param e
     */
    protected abstract void exportCells(int rowIndex, int columnSize, Component row, E e);

    protected abstract void exportCells(List<ListHeaderVo> headers,
                                        int rowIndex, int columnSize, Component row, E e);

    /**
     * 导出表尾
     *
     * @param columnSize
     * @param target
     * @param e
     */
    protected abstract void exportFooters(int columnSize, Component target, E e);

    protected abstract void exportFooters(List<ListHeaderVo> headers, int columnSize, Component target, E e);

    /**
     * 导出指定组件类型
     *
     * @param component
     * @param outputStream
     * @throws Exception
     */
    protected abstract void exportTabularComponent(MeshElement component, OutputStream outputStream) throws Exception;

    protected abstract void exportTabularComponent(ListVo entity,
                                                   MeshElement component, OutputStream outputStream) throws Exception;

    /**
     * 根据提供的数据
     *
     * @param columnSize
     * @param data
     * @param renderer
     * @param outputStream
     * @throws Exception
     */
    public abstract <D> void export(int columnSize, Collection<D> data,
                                    RowRenderer<T, D> renderer, OutputStream outputStream)
            throws Exception;

    /**
     * 导出包括表头的数据
     *
     * @param columnHeaders
     * @param data
     * @param renderer
     * @param outputStream
     * @throws Exception
     */
    public abstract <D> void export(String[] columnHeaders,
                                    final Collection<D> data, RowRenderer<T, D> renderer,
                                    OutputStream outputStream) throws Exception;

    /**
     * 导出分组数据
     *
     * @param columnSize
     * @param data
     * @param renderer
     * @param outputStream
     * @throws Exception
     */
    public abstract <D> void export(int columnSize,
                                    final Collection<Collection<D>> data, GroupRenderer<T, D> renderer,
                                    OutputStream outputStream) throws Exception;

    /**
     * 导出包括表头的分组数据
     *
     * @param columnHeaders
     * @param data
     * @param renderer
     * @param outputStream
     * @throws Exception
     */
    public abstract <D> void export(String[] columnHeaders,
                                    final Collection<Collection<D>> data, GroupRenderer<T, D> renderer,
                                    OutputStream outputStream) throws Exception;

    /**
     * 导出表头
     *
     * @param columnSize
     * @param target
     * @param e
     */
    protected void exportHeaders(ListVo entity, int columnSize,
                                 MeshElement target, E e) {
        List<Component> children = target.getChildren();
        boolean hasAuxhead = false;
        for (Component cmp : children) {
            if (cmp.isVisible()) {
                if (cmp instanceof Auxhead && !hasAuxhead) {
                    exportAuxhead(entity, columnSize, (Auxhead) cmp, e);
                    hasAuxhead = true;
                } else if (cmp instanceof Columns || cmp instanceof Listhead
                        || cmp instanceof Treecols) {
                    exportColumnHeaders(entity.getHeaders(), cmp, e);
                    break;
                }
            }
        }
    }


    /**
     * Export header component
     *
     * @param columnSize
     * @param target
     * @param e
     */
    protected void exportHeaders(int columnSize, MeshElement target, E e) {
        List<Component> children = target.getChildren();
        for (Component cmp : children) {
            if (cmp instanceof Auxhead) {
                exportAuxhead(columnSize, (Auxhead) cmp, e);
            } else if (cmp instanceof Columns || cmp instanceof Listhead) {
                exportColumnHeaders(cmp, e);
            }
        }
    }

    /**
     * Export {@link MeshElement}
     *
     * @param columnSize
     * @param target
     * @param e
     */
    protected void exportRows(int columnSize, MeshElement target, E e) {
        int rowIndex = 0;
        Component rows = null;
        try {
            rows = (Component) invokeComponentGetter(target, "getRows");//for grid
        } catch (ClassCastException ex) {//listbox's getRows will return Integer
        }
        for (Component cmp : rows != null ? rows.getChildren() : target.getChildren()) {
            if (cmp instanceof Listitem || cmp instanceof Row) {
                if (cmp instanceof Listgroup || cmp instanceof Group) {
                    exportGroup(columnSize, cmp, e);
                } else if (cmp instanceof Listgroupfoot || cmp instanceof Groupfoot) {
                    exportGroupfoot(columnSize, cmp, e);
                } else {
                    exportCells(rowIndex++, columnSize, cmp, e);
                }
            }
        }
    }

    /**
     * 导出指定组件的内容
     *
     * @param columnSize
     * @param target
     * @param e
     */
    protected void exportRows(List<ListHeaderVo> headers, int columnSize,
                              MeshElement target, E e) {
        int rowIndex = 0;
        List<Component> rows = null;
        if (target instanceof Grid)
            rows = ((Component) invokeComponentGetter(target, "getRows"))
                    .getChildren();
        else if (target instanceof Listbox)
            rows = target.getChildren();
        else
            // tree
            rows = getTreeRows(target);
        for (Component cmp : rows) {
            if (cmp instanceof Listgroup || cmp instanceof Group) {
                exportGroup(columnSize, cmp, e);
            } else if (cmp instanceof Listgroupfoot || cmp instanceof Groupfoot) {
                exportGroupfoot(columnSize, cmp, e);
            } else if (cmp instanceof Listitem || cmp instanceof Row
                    || cmp instanceof Treerow)
                exportCells(headers, rowIndex++, columnSize, cmp, e);
        }
    }

    private List<Component> getTreeRows(Component root) {
        List<Component> rows = new ArrayList<Component>();

        if (root == null)
            return null;

        for (Component child : root.getChildren()) {
            if (child instanceof Treerow)
                rows.add(child);
            else
                rows.addAll(getTreeRows(child));
        }
        return rows;
    }

    /**
     * 导出指定的组件内容
     *
     * @param component
     * @param outputStream
     * @throws Exception
     */
    public void export(MeshElement component, OutputStream outputStream) throws Exception {
        if (component == null)
            throw new RuntimeException("export target reference is null");

        if (component instanceof Grid || component instanceof Listbox) {
            exportTabularComponent(component, outputStream);
        } else {
            throw new RuntimeException("Component not support export to PDF");
        }
    }

    public void export(ListVo entity, MeshElement component,
                       OutputStream outputStream) throws Exception {
        if (component == null)
            throw new RuntimeException("export target reference is null");
        exportTabularComponent(entity, component, outputStream);
    }

    /**
     * Sets {@link Interceptor}
     *
     * @param interceptor
     */
    public void setInterceptor(Interceptor<E> interceptor) {
        _interceptor = interceptor;
    }

    /**
     * Returns {@link Interceptor}
     *
     * @return
     */
    public Interceptor<E> getInterceptor() {
        return _interceptor;
    }
}
